package qrcodehelper

import (
	"encoding/base64"
	"errors"
	"image"
	"image/color"
	_ "image/gif"
	_ "image/jpeg"
	"image/png"
	"os"

	"github.com/nfnt/resize"
	"github.com/skip2/go-qrcode"
	"golang.org/x/image/draw"
)

type QRCodeGen struct {
	Content string               // 二维码内容，必填
	Level   qrcode.RecoveryLevel // 容错级别(越高越好),Low,Medium,High,Highest，必填
	Size    int                  // 像素单位，必填
	Output  string               // 输出路径，GetQRCodeFile、GetQRCodeCustom、CreateQrCodeWithLogo、CreateQrCodeCustomWithLogo必填
	Logo    string               // logo文件路径，CreateQrCodeWithLogo、CreateQrCodeCustomWithLogo必填
	BColor  color.Color          // 前景颜色
	GColor  color.Color          // 背景颜色
}

// GetQRCodeIO 返回图片字节
func (q *QRCodeGen) GetQRCodeIO() (raw string, err error) {
	err = q.checkParam([]string{})
	if err != nil {
		return
	}
	var png []byte
	png, err = qrcode.Encode(q.Content, q.Level, q.Size)
	if err != nil {
		return
	}
	raw = base64.StdEncoding.EncodeToString(png)
	return
}

// GetQRCodeFile 生成二维码图片
func (q *QRCodeGen) GetQRCodeFile() (err error) {
	err = q.checkParam([]string{"output"})
	if err != nil {
		return
	}
	err = qrcode.WriteFile(q.Content, q.Level, q.Size, q.Output)
	if err != nil {
		return
	}
	return
}

// GetQRCodeCustom 生成二维码自定义颜色 bColor-前景颜色 gColor-背景颜色
func (q *QRCodeGen) GetQRCodeCustomColor() (err error) {
	err = q.checkParam([]string{"output"})
	if err != nil {
		return
	}
	err = qrcode.WriteColorFile(q.Content, q.Level, q.Size, q.BColor, q.GColor, q.Output)
	if err != nil {
		return
	}
	return nil
}

// CreateQrCodeWithLogo 带logo的二维码图片生成
func (q *QRCodeGen) CreateQrCodeWithLogo() (err error) {
	err = q.checkParam([]string{"output", "logo"})
	if err != nil {
		return
	}
	code, err := qrcode.New(q.Content, q.Level)
	if err != nil {
		return
	}
	//设置文件大小并创建画板
	qrcodeImg := code.Image(q.Size)
	outImg := image.NewRGBA(qrcodeImg.Bounds())

	//读取logo文件
	logoFile, err := os.Open(q.Logo)
	if err != nil {
		return
	}
	logoImg, _, err := image.Decode(logoFile)
	if err != nil {
		err = errors.New("logo invalid")
		return
	}
	logoImg = resize.Resize(uint(q.Size/6), uint(q.Size/6), logoImg, resize.Lanczos3)

	//logo和二维码拼接
	draw.Draw(outImg, outImg.Bounds(), qrcodeImg, image.Pt(0, 0), draw.Over)
	offset := image.Pt((outImg.Bounds().Max.X-logoImg.Bounds().Max.X)/2, (outImg.Bounds().Max.Y-logoImg.Bounds().Max.Y)/2)
	draw.Draw(outImg, outImg.Bounds().Add(offset), logoImg, image.Pt(0, 0), draw.Over)
	f, err := os.Create(q.Output)
	if err != nil {
		return
	}
	png.Encode(f, outImg)
	return nil
}

// CreateQrCodeCustomWithLogo 带logo的二维码图片生成自定义颜色
func (q *QRCodeGen) CreateQrCodeWithCircleLogo() (err error) {
	err = q.checkParam([]string{"output", "logo"})
	if err != nil {
		return
	}
	code, err := qrcode.New(q.Content, q.Level)
	if err != nil {
		return err
	}
	qrcodeImg := code.Image(q.Size)
	outImg := image.NewRGBA(qrcodeImg.Bounds())

	logoFile, err := os.Open(q.Logo)
	if err != nil {
		return
	}
	logoImg, _, err := image.Decode(logoFile)
	if err != nil {
		return
	}
	logoImg = resize.Resize(uint(q.Size/6), uint(q.Size/6), logoImg, resize.Lanczos3)

	//添加方形画板
	circleImg := code.Image(q.Size/6 + 5)
	outCircleImg := image.NewRGBA(circleImg.Bounds())
	//logo切为圆形
	draw.DrawMask(outCircleImg, outCircleImg.Bounds(), logoImg, image.ZP, &Circle{image.Pt(q.Size/12, q.Size/12), q.Size / 12}, image.ZP, draw.Over)

	//logo和二维码拼接
	draw.Draw(outImg, outImg.Bounds(), qrcodeImg, image.Pt(0, 0), draw.Over)
	offset := image.Pt((outImg.Bounds().Max.X-outCircleImg.Bounds().Max.X)/2, (outImg.Bounds().Max.Y-logoImg.Bounds().Max.Y)/2)
	draw.Draw(outImg, outImg.Bounds().Add(offset), outCircleImg, image.Pt(0, 0), draw.Over)

	//再次添加画板
	backImg := code.Image(q.Size - q.Size/10)
	outBackImg := image.NewRGBA(backImg.Bounds())
	draw.DrawMask(outBackImg, outBackImg.Bounds(), outImg, image.ZP, &Rectangle{image.Pt((q.Size+(q.Size/10))/2, (q.Size+(q.Size/10))/2), q.Size/2 - q.Size/20, q.Size/2 - q.Size/20}, image.ZP, draw.Over)

	f, err := os.Create(q.Output)
	if err != nil {
		return
	}
	png.Encode(f, outBackImg)
	return nil
}

func (q *QRCodeGen) checkParam(fields []string) (err error) {
	if len(q.Content) <= 0 {
		return errors.New("content invalid")
	}
	if q.Level < 0 {
		return errors.New("level invalid")
	}
	if q.Size <= 0 {
		return errors.New("size invalid")
	}
	if len(fields) <= 0 {
		return
	}
	for _, f := range fields {
		if f == "output" && len(q.Output) <= 0 {
			return errors.New("output invalid")
		} else if f == "logo" && len(q.Logo) <= 0 {
			return errors.New("logo invalid")
		}
	}
	return
}
